home *** CD-ROM | disk | FTP | other *** search
/ Magnum One / Magnum One (Mid-American Digital) (Disc Manufacturing).iso / d12 / v7n21.arc / LOG.ASM < prev    next >
Assembly Source File  |  1988-11-14  |  27KB  |  859 lines

  1. ;=============================================================================
  2. ; LOG maintains a log of system usage in an ASCII file.  Syntax is:
  3. ;    LOG [filespec] [/U]
  4. ; where filespec = Name of and/or path to log file, /U = Uninstall
  5. ;=============================================================================
  6. CODE        SEGMENT    PARA PUBLIC 'CODE'
  7.         ASSUME    CS:CODE
  8.         ORG    100H
  9. BEGIN:        JMP    INITIALIZE
  10.  
  11. PROGRAM        DB    "LOG 1.0 "
  12. COPYRIGHT    DB    "(c) 1988 Ziff Communications Co.",13,10
  13. AUTHOR        DB    "PC Magazine ",254," Jeff Prosise",13,10,"$",26
  14. HANDLE        DW    ?            ;file handle
  15. DOSVERSION    DW    ?            ;DOS version number
  16. INT21H        DD    ?            ;interrupt 21h vector
  17. LEVEL        DW    0            ;EXEC reentrancy level
  18. EXECFLAG    DW    0            ;EXEC flag
  19. XERROR_AX    DW    ?            ;extended error information
  20. XERROR_BX    DW    ?
  21. XERROR_CX    DW    ?
  22. XERROR_DX    DW    ?
  23. XERROR_SI    DW    ?
  24. XERROR_DI    DW    ?
  25. XERROR_DS    DW    ?
  26. XERROR_ES    DW    ?
  27.         DW    3 DUP (0)
  28. ;=============================================================================
  29. ; DOSINT intercepts calls to interrupt 21h.
  30. ;=============================================================================
  31. DOSINT        PROC    FAR
  32.         CMP    AX,4B00H        ;exit immediately if this
  33.         JNE    EXEC0            ;  isn't a call to EXEC
  34.         CMP    CS:[LEVEL],9        ;exit if we've exceeded
  35.         JB    EXEC1            ;  reentrancy limits
  36. EXEC0:        JMP    INT21H
  37. ;--Save registers passed to EXEC by the parent program.
  38. EXEC1:        STI                ;interrupts on
  39.         PUSH    AX            ;save registers
  40.         PUSH    BX
  41.         PUSH    CX
  42.         PUSH    DX
  43.         PUSH    SI
  44.         PUSH    DI
  45.         PUSH    BP
  46.         PUSH    DS
  47.         PUSH    ES
  48. ;--Record the program start/end time and write an entry to the log.
  49.         PUSH    BX            ;save registers set for
  50.         PUSH    DX            ;  EXEC call
  51.         PUSH    DS
  52.         PUSH    ES
  53.         PUSH    CS            ;point DS to code segment
  54.         POP    DS
  55.         ASSUME    DS:CODE
  56.         INC    LEVEL            ;increment reentrancy count
  57.         CALL    RECORD_TIME        ;record the current time
  58.         MOV    DX,OFFSET FILESPEC
  59.         CALL    OPENFILE        ;open the log file
  60.         MOV    SI,LEVEL        ;point SI to start time and
  61.         DEC    SI            ;  DI to end time
  62.         SHL    SI,1
  63.         SHL    SI,1
  64.         ADD    SI,OFFSET TIMES
  65.         MOV    DI,SI
  66.         ADD    DI,4
  67.         MOV    AL,BYTE PTR LEVEL    ;put previous level number
  68.         DEC    AL            ;  in AL
  69.         CALL    WRITE_ENTRY        ;write log entry
  70.         CALL    CLOSEFILE        ;close the log file
  71.         MOV    EXECFLAG,1        ;set EXEC flag
  72.         ASSUME    DS:NOTHING
  73.         POP    ES            ;restore EXEC register
  74.         POP    DS            ;  parameters
  75.         POP    DX
  76.         POP    BX
  77. ;--Record the name of the program just EXECed.
  78.         PUSH    ES            ;save ES and BX for later
  79.         PUSH    BX
  80.         PUSH    DS            ;set ES equal to DS
  81.         POP    ES
  82.         MOV    DI,DX            ;scan for terminating zero
  83.         XOR    AL,AL            ;  in ASCIIZ filename
  84.         MOV    CX,128
  85.         CLD
  86.         REPNE    SCASB
  87.         MOV    BX,127            ;transfer string length
  88.         SUB    BX,CX            ;  to CX
  89.         MOV    CX,BX
  90.         MOV    SI,DI            ;get ending address in SI
  91.         SUB    SI,2            ;set SI to last character
  92.         STD                ;set DF for reverse string ops
  93. EXEC2:        LODSB                ;scan backwards for first
  94.         CMP    AL,"\"            ;  character in filename
  95.         JE    EXEC3
  96.         CMP    AL,":"
  97.         JE    EXEC3
  98.         LOOP    EXEC2
  99.         DEC    SI
  100. EXEC3:        ADD    SI,2            ;point SI to first character
  101.         SUB    BX,CX            ;calculate length of filename
  102.         MOV    CX,BX            ;transfer length to CX
  103.         CLD                ;clear DF again
  104.         PUSH    CS            ;set ES to code segment
  105.         POP    ES
  106.         MOV    AL,13            ;calculate offset into table
  107.         MOV    DL,BYTE PTR CS:[LEVEL]    ;  of program names
  108.         MUL    DL
  109.         MOV    DI,AX
  110.         ADD    DI,OFFSET NAMES
  111.         MOV    AL,CL            ;write filename character
  112.         STOSB                ;  count into table
  113. EXEC5:        LODSB                ;convert lowercase characters
  114.         CMP    AL,"a"            ;  to upper and copy filename
  115.         JB    EXEC6            ;  into table
  116.         CMP    AL,"z"
  117.         JA    EXEC6
  118.         AND    AL,0DFH
  119. EXEC6:        STOSB
  120.         LOOP    EXEC5
  121. ;--Record the command line passed to the program just EXECed.
  122.         POP    BX            ;restore ES and BX
  123.         POP    ES
  124.         LDS    SI,ES:[BX+2]        ;load parm string address
  125.         MOV    CL,[SI]            ;get length
  126.         XOR    CH,CH
  127.         INC    CX
  128.         PUSH    CS            ;point ES:DI to command line
  129.         POP    ES            ;  buffer in code segment
  130.         MOV    DI,OFFSET COMLINE
  131.         REP    MOVSB            ;copy command line
  132. ;--Restore register values originally passed by the parent program.
  133.         POP    ES
  134.         POP    DS
  135.         POP    BP
  136.         POP    DI
  137.         POP    SI
  138.         POP    DX
  139.         POP    CX
  140.         POP    BX
  141.         POP    AX
  142. ;--Save registers again since the DOS 2.X EXEC function destroys them.
  143.         PUSH    AX
  144.         PUSH    BX
  145.         PUSH    CX
  146.         PUSH    DX
  147.         PUSH    SI
  148.         PUSH    DI
  149.         PUSH    BP
  150.         PUSH    DS
  151.         PUSH    ES
  152. ;--Save the values of SS and SP, call EXEC, and restore the stack.
  153.         MOV    CS:[HANDLE],BX        ;calculate an index into
  154.         MOV    BX,CS:[LEVEL]        ;  the STACKSEG and
  155.         DEC    BX            ;  STACKPTR tables
  156.         SHL    BX,1
  157.         CLI                    ;disable interrupts and
  158.         MOV    WORD PTR CS:[STACKSEG][BX],SS    ;  save the SS and SP
  159.         MOV    WORD PTR CS:[STACKPTR][BX],SP    ;  registers
  160.         MOV    BX,CS:[HANDLE]
  161.         PUSHF                ;PUSH the flags register and
  162.         CALL    INT21H            ;  call DOS EXEC
  163.         LAHF                ;store flags temporarily in AH
  164.         MOV    BX,CS:[LEVEL]        ;recalculate table index
  165.         DEC    BX
  166.         SHL    BX,1
  167.         CLI                    ;disable interrupts and
  168.         MOV    SS,WORD PTR CS:[STACKSEG][BX]    ;  restore SS and SP
  169.         MOV    SP,WORD PTR CS:[STACKPTR][BX]
  170.         STI
  171.         SAHF                ;restore flags
  172. ;--Restore the registers to their conditions before EXEC was called.
  173.         POP    ES
  174.         POP    DS
  175.         POP    BP
  176.         POP    DI
  177.         POP    SI
  178.         POP    DX
  179.         POP    CX
  180.         POP    BX
  181.         POP    AX
  182. ;--Save registers again while post-processing is performed.
  183.         PUSHF                ;then save it for exit
  184.         PUSH    AX            ;save general registers
  185.         PUSH    BX
  186.         PUSH    CX
  187.         PUSH    DX
  188.         PUSH    SI
  189.         PUSH    DI
  190.         PUSH    BP
  191.         PUSH    DS
  192.         PUSH    ES
  193. ;--Save extended error information if DOS version is 3.10 or later.
  194.         PUSH    CS            ;set DS to code segment
  195.         POP    DS
  196.         ASSUME    DS:CODE
  197.         CMP    DOSVERSION,030AH    ;skip if not 3.10 or later
  198.         JB    EXEC7
  199.         PUSH    DS            ;save DS
  200.         MOV    AH,59H            ;get extended error
  201.         XOR    BX,BX            ;  information
  202.         INT    21H
  203.         MOV    CS:[XERROR_DS],DS    ;save return value of DS
  204.         POP    DS            ;set DS to code segment again
  205.         MOV    XERROR_AX,AX        ;save remaining register
  206.         MOV    XERROR_BX,BX        ;  values in XERROR array
  207.         MOV    XERROR_CX,CX
  208.         MOV    XERROR_DX,DX
  209.         MOV    XERROR_SI,SI
  210.         MOV    XERROR_DI,DI
  211.         MOV    XERROR_ES,ES
  212. ;--Record the program start/end time and write an entry to the log.
  213. EXEC7:        DEC    LEVEL            ;decrement reentrancy count
  214.         CALL    RECORD_TIME        ;record the current time
  215.         MOV    DX,OFFSET FILESPEC
  216.         CALL    OPENFILE        ;open the log file
  217.         MOV    SI,LEVEL        ;point SI to start time and
  218.         INC    SI            ;  DI to end time
  219.         SHL    SI,1
  220.         SHL    SI,1
  221.         ADD    SI,OFFSET TIMES
  222.         MOV    DI,SI
  223.         SUB    DI,4
  224.         MOV    AL,BYTE PTR LEVEL    ;put previous level number
  225.         INC    AL            ;  in AL
  226.         CALL    WRITE_ENTRY        ;write log entry
  227.         CALL    CLOSEFILE        ;close the log file
  228.         MOV    EXECFLAG,0        ;clear EXEC flag
  229. ;--Restore extended error information if DOS version is 3.10 or later.
  230.         CMP    DOSVERSION,030AH    ;skip if not 3.10 or later
  231.         JB    EXEC8
  232.         MOV    AX,5D0AH        ;restore information with
  233.         MOV    DX,OFFSET XERROR_AX    ;  undocumented function 5Dh
  234.         INT    21H
  235. ;--Restore registers a final time and exit to the parent program.
  236.         ASSUME    DS:NOTHING
  237. EXEC8:        POP    ES            ;restore registers
  238.         POP    DS
  239.         POP    BP
  240.         POP    DI
  241.         POP    SI
  242.         POP    DX
  243.         POP    CX
  244.         POP    BX
  245.         POP    AX
  246.         POPF
  247. DOSEXIT:    IRET
  248. DOSINT        ENDP
  249. ;-----------------------------------------------------------------------------
  250. ; OPENFILE opens an existing log file or creates a new one.
  251. ; Entry:  DS - code segment
  252. ; Exit:   CF: clear-opened/created, set-file could not be opened/created
  253. ;-----------------------------------------------------------------------------
  254. ADDRSPEC    DW    ?
  255. CALLFLAG    DB    0
  256.  
  257. OPENFILE    PROC    NEAR
  258.         ASSUME    DS:CODE
  259.         MOV    ADDRSPEC,DX        ;save filespec address
  260.         MOV    AX,3D02H        ;attempt to open an existing
  261.         INT    21H            ;  log file
  262.         JC    OPEN3            ;branch if call failed
  263.         MOV    HANDLE,AX        ;store file handle
  264.         MOV    BX,AX            ;transfer it to BX
  265.         MOV    AX,4202H        ;position file pointer at
  266.         XOR    CX,CX            ;  the end of the file
  267.         XOR    DX,DX
  268.         INT    21H
  269.         CMP    CALLFLAG,0        ;write date to log file
  270.         JNE    OPEN1            ;  if this is the first
  271.         INC    CALLFLAG        ;  call to OPENFILE
  272. OPEN0:        CALL    WRITE_DATE
  273.         CALL    WRITE_HEADER        ;then write header line
  274. OPEN1:        CLC                ;clear CF for exit
  275. OPEN2:        RET                ;exit
  276. ;--Call to open an existing file failed.  Create a new one and initialize it.
  277. OPEN3:        MOV    AH,3CH            ;attempt to create a new
  278.         XOR    CX,CX            ;  file of the same name
  279.         MOV    DX,ADDRSPEC
  280.         INT    21H
  281.         JC    OPEN2            ;exit on error
  282.         MOV    HANDLE,AX        ;store file handle
  283.         MOV    BX,AX            ;transfer it to BX
  284.         MOV    CALLFLAG,1        ;set entry flag
  285.         MOV    AH,40H            ;write copyright text
  286.         MOV    CX,70
  287.         MOV    DX,OFFSET PROGRAM
  288.         INT    21H
  289.         JMP    OPEN0            ;exit
  290. OPENFILE    ENDP
  291. ;-----------------------------------------------------------------------------
  292. ; CLOSEFILE closes the log file.
  293. ;-----------------------------------------------------------------------------
  294. CLOSEFILE    PROC    NEAR
  295.         ASSUME    DS:CODE
  296.         MOV    AH,3EH
  297.         MOV    BX,HANDLE
  298.         INT    21H
  299.         RET
  300. CLOSEFILE    ENDP
  301. ;-----------------------------------------------------------------------------
  302. ; WRITE_ENTRY writes a one-line entry to the log file.
  303. ;   Entry:  DS:SI - start time
  304. ;           DS:DI - end time
  305. ;           AL    - last reentrancy level
  306. ;-----------------------------------------------------------------------------
  307. LASTLEVEL    DB    ?
  308.  
  309. WRITE_ENTRY    PROC    NEAR
  310.         ASSUME    DS:CODE
  311.         MOV    LASTLEVEL,AL        ;store last level number
  312.         CALL    WRITE_TIME        ;write start time
  313.         MOV    CX,4
  314.         CALL    WRITE_SPACES
  315.         PUSH    SI            ;save start time
  316.         MOV    SI,DI            ;write end time
  317.         CALL    WRITE_TIME
  318.         MOV    CX,3
  319.         CALL    WRITE_SPACES
  320.         POP    SI            ;retrieve start time
  321.         CALL    WRITE_DIFF        ;write elapsed time
  322.         MOV    CX,5
  323.         CALL    WRITE_SPACES
  324.         MOV    AL,LASTLEVEL        ;write reentrancy level
  325.         MOV    BL,1
  326.         CALL    WRITE_NUM
  327.         MOV    CX,6
  328.         CALL    WRITE_SPACES
  329.         MOV    AL,13            ;calculate offset into
  330.         MUL    LASTLEVEL        ;  program name table
  331.         MOV    DX,AX
  332.         ADD    DX,OFFSET NAMES
  333.         MOV    AH,40H            ;write program name
  334.         MOV    BX,DX
  335.         MOV    CL,[BX]
  336.         XOR    CH,CH
  337.         PUSH    CX
  338.         MOV    BX,HANDLE
  339.         INC    DX
  340.         INT    21H
  341.         POP    BX
  342.         CMP    EXECFLAG,0        ;exit now if this entry is
  343.         JE    NOPARMS            ;  a return from EXEC
  344.         MOV    CX,12            ;calculate number of spaces
  345.         SUB    CX,BX            ;  to skip
  346.         ADD    CX,5
  347.         CALL    WRITE_SPACES
  348.         MOV    BX,OFFSET COMLINE    ;write command line passed
  349.         MOV    CL,[BX]            ;  to the last program
  350.         XOR    CH,CH            ;  EXECed to the log file
  351.         JCXZ    NOPARMS
  352.         MOV    AH,40H
  353.         MOV    BX,HANDLE
  354.         MOV    DX,OFFSET COMLINE+1
  355.         INT    21H
  356. NOPARMS:    CALL    WRITE_CRLF        ;end line with CR/LF
  357.         RET
  358. WRITE_ENTRY    ENDP
  359. ;-----------------------------------------------------------------------------
  360. ; WRITE_DATE writes the current date to the log file.
  361. ;-----------------------------------------------------------------------------
  362. MONTHS        DB    "JanFebMarAprMayJunJulAugSepOctNovDec"
  363. CENTURY        DB    "19"
  364.  
  365. WRITE_DATE    PROC    NEAR
  366.         ASSUME    DS:CODE
  367.         CALL    WRITE_CRLF        ;skip one line
  368.         MOV    AH,2AH            ;get date from DOS
  369.         INT    21H
  370.         PUSH    CX            ;save it
  371.         PUSH    DX
  372.         MOV    AL,DL            ;write day of the month to
  373.         XOR    BL,BL            ;  the log file
  374.         CALL    WRITE_NUM
  375.         MOV    CX,1            ;skip a space
  376.         CALL    WRITE_SPACES
  377.         POP    DX            ;retrieve month from stack
  378.         DEC    DH            ;calculate offset into MONTH
  379.         MOV    CL,DH            ;  table of text for the
  380.         XOR    CH,CH            ;  current month
  381.         MOV    DX,OFFSET MONTHS
  382.         JCXZ    WRDATE2
  383. WRDATE1:    ADD    DX,3
  384.         LOOP    WRDATE1
  385. WRDATE2:    MOV    AH,40H            ;write month to log file
  386.         MOV    BX,HANDLE
  387.         MOV    CX,3
  388.         INT    21H
  389.         MOV    CX,1            ;skip a space
  390.         CALL    WRITE_SPACES
  391.         MOV    AH,40H            ;write "19" portion of year
  392.         MOV    CX,2
  393.         MOV    DX,OFFSET CENTURY
  394.         INT    21H
  395.         POP    CX            ;retrieve year from stack
  396.         SUB    CX,1900            ;subtract century portion
  397.         MOV    AL,CL            ;write year to the log file
  398.         XOR    BL,BL
  399.         CALL    WRITE_NUM
  400.         CALL    WRITE_CRLF        ;finish with CRLF pairs
  401.         CALL    WRITE_CRLF
  402.         RET
  403. WRITE_DATE    ENDP
  404.  
  405. ;-----------------------------------------------------------------------------
  406. ; WRITE_TIME writes the time to the log file.  Entry:  DS:SI - time
  407. ;-----------------------------------------------------------------------------
  408. COLON        DB    ":"
  409.  
  410. WRITE_TIME    PROC    NEAR
  411.         ASSUME    DS:CODE
  412.         MOV    AL,[SI+1]        ;write hours to log file
  413.         MOV    BL,1
  414.         CALL    WRITE_NUM
  415.         MOV    AH,40H            ;write ":" to separate
  416.         MOV    BX,HANDLE        ;  hours and minutes
  417.         MOV    CX,1
  418.         MOV    DX,OFFSET COLON
  419.         INT    21H
  420.         MOV    AL,[SI]            ;write minutes to log file
  421.         XOR    BL,BL
  422.         CALL    WRITE_NUM
  423.         RET
  424. WRITE_TIME    ENDP
  425. ;-----------------------------------------------------------------------------
  426. ; WRITE_DIFF writes the elapsed time to the log file.
  427. ;   Entry:  DS:SI - start time,  DS:DI - end time
  428. ;-----------------------------------------------------------------------------
  429. DELTA        DB    3 DUP (?)
  430.  
  431. WRITE_DIFF    PROC    NEAR
  432.         ASSUME    DS:CODE
  433.         MOV    AL,[DI]            ;retrieve starting and ending
  434.         MOV    BL,[SI]            ;  seconds, minutes, and
  435.         MOV    CL,[DI+1]        ;  hours
  436.         MOV    DL,[SI+1]
  437.         MOV    AH,[DI+2]
  438.         MOV    BH,[SI+2]
  439.         CMP    AH,BH            ;if ending seconds is less
  440.         JAE    WRDIFF1            ;  than starting, add 60 to
  441.         ADD    AH,60            ;  ending seconds and
  442.         DEC    AL            ;  decrement ending minutes
  443. WRDIFF1:    CMP    AL,BL            ;if ending minutes is less
  444.         JGE    WRDIFF2            ;  than starting, add 60 to
  445.         ADD    AL,60            ;  ending minutes and
  446.         DEC    CL            ;  decrement ending hours
  447. WRDIFF2:    CMP    CL,DL            ;if ending hours is less
  448.         JGE    WRDIFF3            ;  than starting, add 24
  449.         ADD    CL,24            ;  to ending hours
  450. WRDIFF3:    SUB    AH,BH            ;calculate seconds difference
  451.         MOV    DELTA+2,AH        ;store it
  452.         SUB    AL,BL            ;calculate minutes difference
  453.         MOV    DELTA,AL        ;store it
  454.         SUB    CL,DL            ;calculate hours difference
  455.         MOV    DELTA+1,CL        ;store it
  456.         MOV    SI,OFFSET DELTA        ;write hours and minutes to
  457.         CALL    WRITE_TIME        ;  the log file
  458.         MOV    AH,40H            ;write ":" to log file
  459.         MOV    BX,HANDLE
  460.         MOV    CX,1
  461.         MOV    DX,OFFSET COLON
  462.         INT    21H
  463.         MOV    AL,[SI+2]        ;write seconds to log file
  464.         XOR    BL,BL
  465.         CALL    WRITE_NUM
  466.         RET
  467. WRITE_DIFF    ENDP
  468. ;-----------------------------------------------------------------------------
  469. ; WRITE_NUM converts a binary byte value to ASCII and writes it to the log
  470. ; file.  Value must be between 0 and 99, inclusive.
  471. ; Entry:  AL - byte value, BL - 0 = include leading zeroes, 1 = don't include
  472. ;-----------------------------------------------------------------------------
  473. FILEWORD    DW    ?
  474.  
  475. WRITE_NUM    PROC    NEAR
  476.         ASSUME    DS:CODE
  477.         AAM                ;convert to BCD in AX
  478.         ADD    AX,3030H        ;convert to ASCII
  479.         OR    BL,BL            ;convert leading zero to
  480.         JZ    WRNUM1            ;  space if BL = 1 on
  481.         CMP    AH,30H            ;  entry
  482.         JNE    WRNUM1
  483.         MOV    AH,20H
  484. WRNUM1:        XCHG    AH,AL            ;swap bytes
  485.         MOV    FILEWORD,AX        ;store them
  486.         MOV    AH,40H            ;then write them to the
  487.         MOV    BX,HANDLE        ;  log file
  488.         MOV    CX,2
  489.         MOV    DX,OFFSET FILEWORD
  490.         INT    21H
  491.         RET
  492. WRITE_NUM    ENDP
  493. ;-----------------------------------------------------------------------------
  494. ; WRITE_HEADER writes the column titles to the log file.
  495. ;-----------------------------------------------------------------------------
  496. TITLES        DB    "START",5 DUP (32),"END",5 DUP (32)
  497.         DB    "ELAPSED",4 DUP (32),"LEVEL",4 DUP (32)
  498.         DB    "PROGRAM",10 DUP (32),"PARAMETERS",13,10
  499. EQUALSIGN    DB    "="
  500.  
  501. WRITE_HEADER    PROC    NEAR
  502.         ASSUME    DS:CODE
  503.         MOV    AH,40H            ;write column titles
  504.         MOV    BX,HANDLE
  505.         MOV    CX,67
  506.         MOV    DX,OFFSET TITLES
  507.         INT    21H
  508.         MOV    CX,79            ;write row of "=" symbols
  509. WRHEAD1:    PUSH    CX
  510.         MOV    AH,40H
  511.         MOV    CX,1
  512.         MOV    DX,OFFSET EQUALSIGN
  513.         INT    21H
  514.         POP    CX
  515.         LOOP    WRHEAD1
  516.         CALL    WRITE_CRLF        ;end it with a CR/LF pair
  517.         RET
  518. WRITE_HEADER    ENDP
  519. ;-----------------------------------------------------------------------------
  520. ; WRITE_CRLF writes a carriage return / line feed to the log file.
  521. ;-----------------------------------------------------------------------------
  522. CRLF        DB    13,10
  523.  
  524. WRITE_CRLF    PROC    NEAR
  525.         ASSUME    DS:CODE
  526.         MOV    AH,40H
  527.         MOV    BX,HANDLE
  528.         MOV    CX,2
  529.         MOV    DX,OFFSET CRLF
  530.         INT    21H
  531.         RET
  532. WRITE_CRLF    ENDP
  533. ;-----------------------------------------------------------------------------
  534. ; WRITE_SPACES writes CX spaces to the log file.
  535. ;-----------------------------------------------------------------------------
  536. SPACE        DB    32
  537.  
  538. WRITE_SPACES    PROC    NEAR
  539.         ASSUME    DS:CODE
  540.         PUSH    CX            ;save counter
  541.         MOV    AH,40H            ;write one space
  542.         MOV    BX,HANDLE
  543.         MOV    CX,1
  544.         MOV    DX,OFFSET SPACE
  545.         INT    21H
  546.         POP    CX            ;retrieve count
  547.         LOOP    WRITE_SPACES        ;loop until done
  548.         RET
  549. WRITE_SPACES    ENDP
  550. ;-----------------------------------------------------------------------------
  551. ; RECORD_TIME records the current time in the slot designated by LEVEL.
  552. ;-----------------------------------------------------------------------------
  553. RECORD_TIME    PROC    NEAR
  554.         ASSUME    DS:CODE
  555.         MOV    AH,2CH            ;get current time
  556.         INT    21H
  557.         MOV    BX,LEVEL        ;calculate offset into table
  558.         SHL    BX,1            ;  of start/end times
  559.         SHL    BX,1
  560.         MOV    WORD PTR TIMES[BX],CX    ;store current time
  561.         MOV    BYTE PTR TIMES[BX+2],DH
  562.         RET
  563. RECORD_TIME    ENDP
  564. ;=============================================================================
  565. ; 418-byte buffer area used after LOG becomes resident.
  566. ;=============================================================================
  567. PC        =    $
  568. FILESPEC    =    PC            ;filespec buffer
  569. PC        =    PC + 80
  570. NAMES        =    PC            ;storage array for program
  571. PC        =    PC + 130        ;  names
  572. TIMES        =    PC            ;storage array for program
  573. PC        =    PC + 40            ;  start/end times
  574. STACKSEG    =    PC            ;storage array for SS
  575. PC        =    PC + 20            ;  register values
  576. STACKPTR    =    PC            ;storage array for SP
  577. PC        =    PC + 20            ;  register values
  578. COMLINE        =    PC            ;command line parameter buffer
  579. PC        =    PC + 128
  580. LASTBYTE    =    PC
  581. ;=============================================================================
  582. ; INITIALIZE installs or uninstalls the program.
  583. ;=============================================================================
  584. ERRMSG1        DB    "Usage: LOG [filespec] [/U]$"
  585. ERRMSG2        DB    "Not Installed$"
  586. ERRMSG3        DB    "Cannot Uninstall$"
  587. ERRMSG4        DB    "Already Installed$"
  588. ERRMSG5        DB    "Invalid Filespec$"
  589. OUTTEXT        DB    "Uninstalled$"
  590. DEFNAME        DB    "\USAGE.LOG",13
  591. IDLETEXT    DB    6,"<idle>"
  592. INSTALLED    DB    0
  593.  
  594. INITIALIZE    PROC    NEAR
  595.         ASSUME    CS:CODE, DS:CODE
  596. ;--See if a copy of LOG is already resident in memory.
  597.         CLD                ;clear DF for string ops
  598.         MOV    WORD PTR [BEGIN],0    ;initialize fingerprint
  599.         XOR    BX,BX            ;zero BX for start
  600.         MOV    AX,CS            ;keep CS value in AX
  601. INIT1:        INC    BX            ;increment search segment value
  602.         MOV    ES,BX
  603.         CMP    AX,BX            ;not installed if current
  604.         JE    PARSE1            ;  segment is reached
  605.         MOV    SI,OFFSET BEGIN        ;search this segment for ASCII
  606.         MOV    DI,SI            ;  fingerprint
  607.         MOV    CX,16
  608.         REPE    CMPSB
  609.         JNE    INIT1            ;loop back if not found
  610.         MOV    INSTALLED,1        ;set installed flag
  611. ;--Parse the command line for entries.
  612. PARSE1:        MOV    SI,81H            ;point SI to command line
  613. PARSE2:        LODSB                ;get a character
  614.         CMP    AL,20H            ;skip it if it's a space
  615.         JE    PARSE2
  616.         CMP    AL,0DH            ;exit loop when a carriage
  617.         JE    NOSPEC            ;  return is encountered
  618.         CMP    AL,"/"            ;check for forward slash
  619.         JNE    QUALIFY            ;anything else is a filespec
  620.         LODSB                ;get the next character
  621.         AND    AL,0DFH            ;capitalize it
  622.         CMP    AL,"U"            ;branch to uninstall code if
  623.         JE    UNINSTALL        ;  character is a "U"
  624. ;--An error was encountered in parsing.  Display error message and exit.
  625.         MOV    DX,OFFSET ERRMSG1    ;load message address
  626. ERROR_EXIT:    MOV    AH,9            ;display error message
  627.         INT    21H
  628.         MOV    AX,4C01H        ;exit with ERRORLEVEL = 1
  629.         INT    21H
  630. ;--Uninstall the program.
  631. UNINSTALL:    MOV    DX,OFFSET ERRMSG2    ;error if program isn't
  632.         CMP    INSTALLED,0        ;  installed
  633.         JE    ERROR_EXIT
  634.         CALL    REMOVE            ;call uninstall routine
  635.         MOV    DX,OFFSET ERRMSG3    ;error if uninstall failed
  636.         JC    ERROR_EXIT
  637.         MOV    DX,OFFSET OUTTEXT    ;display "Uninstalled"
  638.         MOV    AH,9            ;  message
  639.         INT    21H
  640.         MOV    AX,4C00H        ;exit with ERRORLEVEL = 0
  641.         INT    21H
  642. ;--Convert the filespec into a fully qualified filename.
  643. NOSPEC:        MOV    SI,OFFSET DEFNAME+1    ;Use default filespec
  644. QUALIFY:    MOV    DX,OFFSET ERRMSG4    ;error if program is already
  645.         CMP    INSTALLED,0        ;  installed
  646.         JNE    ERROR_EXIT
  647.         DEC    SI            ;point DS:SI to filespec
  648.         PUSH    CS            ;point ES:DI to temporary
  649.         POP    ES            ;  filespec buffer
  650.         MOV    DI,OFFSET CWDIR+80
  651.         CALL    GENSPEC            ;generate complete filespec
  652.         MOV    DX,OFFSET ERRMSG5    ;exit if error flag is set
  653.         JC    ERROR_EXIT        ;  on return
  654.         MOV    DI,OFFSET CWDIR+80    ;find the terminating zero in
  655.         XOR    AL,AL            ;  the ASCIIZ string
  656.         MOV    CX,80
  657.         REPNE    SCASB
  658.         DEC    DI
  659.         CMP    BYTE PTR [DI-1],"\"    ;append default filename if
  660.         JNE    TESTSPEC        ;  last character is a
  661.         MOV    SI,OFFSET DEFNAME+1    ;  backslash
  662.         MOV    CX,9
  663.         REP    MOVSB
  664.         XOR    AL,AL            ;write ASCIIZ terminator
  665.         STOSB
  666. ;--Test the filespec and create the log file if it doesn't already exist.
  667. TESTSPEC:    MOV    DX,OFFSET CWDIR+80
  668.         CALL    OPENFILE        ;open/create log file
  669.         MOV    DX,OFFSET ERRMSG5    ;error if file could not
  670.         JC    ERROR_EXIT        ;  be opened
  671.         CALL    CLOSEFILE        ;close log file
  672.         CALL    RECORD_TIME        ;record starting time
  673.         MOV    AX,3000H        ;record version of DOS
  674.         INT    21H            ;  LOG is running under
  675.         XCHG    AH,AL
  676.         MOV    DOSVERSION,AX
  677. ;--Hook into interrupt 21h and deallocate the program's environment block.
  678.         MOV    AX,3521H        ;save old interrupt vector
  679.         INT    21H
  680.         MOV    WORD PTR INT21H,BX
  681.         MOV    WORD PTR INT21H[2],ES
  682.         MOV    AX,2521H        ;then set the new vector
  683.         MOV    DX,OFFSET DOSINT
  684.         INT    21H
  685.         MOV    AX,DS:[2CH]        ;deallocate the program's
  686.         MOV    ES,AX            ;  environment block
  687.         MOV    AH,49H
  688.         INT    21H
  689. ;--Initialize buffer areas.
  690.         PUSH    CS            ;point ES to code segment
  691.         POP    ES
  692.         MOV    SI,OFFSET CWDIR+80    ;copy filepsec string to
  693.         MOV    DI,OFFSET FILESPEC    ;  filespec buffer
  694. COPYLOOP:    LODSB
  695.         STOSB
  696.         OR    AL,AL
  697.         JNZ    COPYLOOP
  698.         MOV    SI,OFFSET IDLETEXT    ;copy "<idle>" string to
  699.         MOV    DI,OFFSET NAMES        ;  program names buffer
  700.         MOV    CX,7
  701.         REP    MOVSB
  702. ;--Display copyright notice, then terminate and remain resident in memory.
  703.         MOV    AH,9            ;display copyright
  704.         MOV    DX,OFFSET PROGRAM
  705.         INT    21H
  706.         MOV    AX,3100H
  707.         MOV    DX,(OFFSET LASTBYTE - OFFSET CODE + 15) SHR 4
  708.         INT    21H
  709. INITIALIZE    ENDP
  710. ;-----------------------------------------------------------------------------
  711. ; REMOVE deallocates the memory block addressed by ES and restores the
  712. ; interrupt vector displaced on installation.
  713. ; Entry:  ES - segment to release
  714. ; Exit:   CF clear - program uninstalled, CF set   - can't uninstall
  715. ;-----------------------------------------------------------------------------
  716. REMOVE          PROC    NEAR
  717.         MOV    CX,ES            ;abort if the interrupt 21h
  718.         MOV    AX,3521H        ;  vector has been altered
  719.         INT    21H            ;  since installation
  720.         MOV    AX,ES
  721.         CMP    AX,CX
  722.         JNE    REMOVE_ERROR
  723.         MOV    ES,CX
  724.         MOV     AH,49H                  ;free memory given to
  725.         INT     21h                     ;  original program block
  726.         JC    REMOVE_ERROR        ;branch on error
  727.         PUSH    DS            ;restore interrupt vector
  728.         ASSUME  DS:NOTHING
  729.         MOV    AX,2521H
  730.         LDS    DX,ES:[INT21H]
  731.         INT    21H
  732.         POP     DS
  733.         ASSUME  DS:CODE
  734.         NOT     WORD PTR ES:[BEGIN]     ;destroy ASCII fingerprint
  735.         CLC                ;clear CF for exit
  736.         RET
  737. REMOVE_ERROR:    STC                ;set CF to indicate program
  738.         RET                ;  couldn't be uninstalled
  739. REMOVE          ENDP
  740. ;-----------------------------------------------------------------------------
  741. ; GENSPEC generates a fully qualified filename.
  742. ; Entry: DS:SI - filename, ES:DI - filespec buffer
  743. ; Exit:  CF clear - drive/directory valid, CF set - drive/directory invalid
  744. ;-----------------------------------------------------------------------------
  745. ADDRIN        DW    ?            ;input string address
  746. ADDROUT        DW    ?            ;output string address
  747. DEFDRIVE    DB    ?            ;default drive
  748. ADDRSLASH    DW    ?            ;address of last backslash
  749.  
  750. GENSPEC        PROC    NEAR
  751.         MOV    ADDROUT,DI        ;save output buffer address
  752. ;--Save the current drive and directory.
  753.         PUSH    SI            ;save SI
  754.         MOV    AH,19H            ;get current drive and
  755.         INT    21H            ;  save it
  756.         MOV    DEFDRIVE,AL
  757.         MOV    AH,47H            ;get current directory and
  758.         XOR    DL,DL            ;  save it
  759.         MOV    SI,OFFSET CWDIR+1
  760.         INT    21H
  761.         POP    SI            ;restore SI
  762. ;--Check for a drive designator and supply default drive code if needed.
  763.         CMP    BYTE PTR [SI+1],":"    ;see if drive designator
  764.         JNE    GEN1            ;  appears in filespec
  765.         LODSW                ;  buffer and capitalize
  766.         AND    AL,0DFH            ;  drive letter if it does
  767.         JMP    SHORT GEN2
  768. GEN1:        MOV    AL,DEFDRIVE        ;get default drive letter
  769.         ADD    AL,"A"            ;convert to ASCII
  770.         MOV    AH,":"            ;append colon
  771. GEN2:        STOSW                ;and output drive spec
  772. ;--Scan the input filespec for the rightmost backslash character.
  773.         MOV    ADDRIN,SI        ;save filespec address
  774.         XOR    CX,CX            ;initialize counter
  775. GEN3:        LODSB                ;get next byte
  776.         CMP    AL,0DH            ;scan finished if character
  777.         JE    GEN4            ;  is a carriage return or
  778.         CMP    AL,20H            ;  space
  779.         JE    GEN4
  780.         CMP    AL,"\"            ;loop back if it's anything
  781.         JNE    GEN3            ;  but a backslash
  782.         MOV    CX,SI            ;save address of backslash
  783.         DEC    CX            ;  character and return to
  784.         JMP    GEN3            ;  scan loop
  785. ;--Copy everything up to the last backslash to the output buffer.
  786. GEN4:        MOV    ADDRSLASH,CX        ;save backslash address
  787.         MOV    SI,ADDRIN        ;point SI back to filespec
  788.         OR    CX,CX            ;terminate string if no
  789.         JZ    GEN6            ;  backslashes found
  790.         CMP    SI,CX            ;copy leading backslash if
  791.         JE    GEN5            ;  if it was the only one
  792.         SUB    CX,SI            ;otherwise copy everything
  793.         DEC    CX            ;  up to the last backslash
  794.         JCXZ    GEN5
  795.         REP    MOVSB
  796. GEN5:        MOVSB
  797. GEN6:        XOR    AL,AL            ;append binary zero to form
  798.         STOSB                ;  ASCIIZ string
  799. ;--Change to the drive and directory specified and get a full pathname.
  800.         MOV    AH,0EH            ;switch current drive
  801.         MOV    DI,ADDROUT
  802.         MOV    DL,[DI]
  803.         SUB    DL,"A"
  804.         INT    21H
  805.         CMP    BYTE PTR [DI+2],0
  806.         JE    GEN7
  807.         MOV    AH,3BH            ;set current directory to
  808.         MOV    DX,ADDROUT        ;  the one just formulated
  809.         ADD    DX,2            ;  and exit if the call fails
  810.         INT    21H
  811.         JC    GEN_EXIT
  812. GEN7:        MOV    AH,47H            ;now request a complete
  813.         MOV    SI,ADDROUT        ;  directory string from
  814.         MOV    DL,[SI]            ;  DOS
  815.         SUB    DL,40H
  816.         ADD    SI,3
  817.         MOV    BYTE PTR [SI-1],"\"
  818.         INT    21H
  819.         JC    GEN_EXIT        ;exit on error
  820. ;--Reset the default drive and directory.
  821.         MOV    AH,0EH            ;reset default drive
  822.         MOV    DL,DEFDRIVE
  823.         INT    21H
  824.         MOV    AH,3BH            ;finish up by setting the
  825.         MOV    DX,OFFSET CWDIR        ;  current directory to what
  826.         INT    21H            ;  it was when we started
  827.         JC    GEN_EXIT
  828. ;--Append the filename to the directory specification if one was entered.
  829.         MOV    DI,ADDROUT        ;find ASCIIZ terminator byte
  830.         XOR    AL,AL            ;  in output buffer
  831.         MOV    CX,80
  832.         REPNE    SCASB
  833.         DEC    DI
  834.         CMP    BYTE PTR [DI-1],"\"    ;insert a backslash if
  835.         JE    GEN8            ;  there's not one at the
  836.         MOV    AL,"\"            ;  end of the string
  837.         STOSB
  838. GEN8:        MOV    SI,ADDRSLASH        ;point SI to the first
  839.         OR    SI,SI            ;  character in the
  840.         JNZ    GEN9            ;  input filename
  841.         MOV    SI,ADDRIN
  842.         JMP    SHORT GEN10
  843. GEN9:        INC    SI
  844. GEN10:        LODSB                ;copy characters until a
  845.         CMP    AL,0DH            ;  carriage return or space
  846.         JE    GEN11            ;  delimiter is reached
  847.         CMP    AL,20H
  848.         JE    GEN11
  849.         STOSB
  850.         JMP    GEN10
  851. GEN11:        XOR    AL,AL            ;append terminating zero byte
  852.         STOSB                ;  to string
  853.         CLC                ;clear CF and exit
  854. GEN_EXIT:    RET
  855. GENSPEC        ENDP
  856. CWDIR        DB    "\"            ;directory string buffer
  857. CODE        ENDS
  858.         END    BEGIN
  859.